#pragma once

typedef WCHAR * (__thiscall * VALUETOSTRING)(LPWSTR lpwszStr, UINT nLen);
typedef void * (__thiscall * GETHWNDELEMENT)();
typedef void * (__thiscall * GETELEMENTVALUE1)(void * pPropertyInfo, int nType);
typedef void * (__thiscall * GETELEMENTVALUE2)(void * pPropertyInfo, int nType, void * pUpdateCache);
typedef void (__thiscall * VALUERELEASE)();
typedef void (__thiscall * VALUEZERORELEASE)();
typedef IUnknown * (__thiscall * ELEMENTGETDATACONTEXT)(void ** pValue);
typedef long (__stdcall * INITTHREAD1)(UINT n);
typedef long (__stdcall * INITTHREAD2)(void);
typedef long (__stdcall * INITGADGETCOMPONENT)(int n);
DECLARE_HANDLE(HGADGET);
typedef BOOL (WINAPI * GETGADGETRECT)(HGADGET hGadget,LPRECT lprect,DWORD dwFlags); // 0xC

#define CT_STDCALL 0
#define CT_CDECL 1
#define CT_THISCALL 2

extern UINT g_nGetGadgetRectMsg;
extern void * CallFunction(void * pFunction, int nCallType, int nParamCount, ...);

struct DECLSPEC_NOVTABLE IClassInfo 
{
public:
	virtual long CreateInstance(void ** ppElement) = 0;
	virtual void * EnumPropertyInfo(DWORD dwIndex) = 0;
	virtual DWORD GetPICount() = 0;
	virtual UINT GetGlobalIndex() = 0;
	virtual IClassInfo * GetBaseClass() = 0;
	virtual LPCWSTR GetName() = 0;
	virtual bool IsValidProperty(void *pProperty) = 0;
	virtual bool IsSubclassOf(const void *pClassInfo) = 0;
	virtual void Destroy() = 0;
};

class DynamicArray
{
public:
	virtual ~DynamicArray();
	DWORD unknown1;
	DWORD m_dwCount;
	DWORD m_dwMaximum;
	void ** m_pElements;
	DWORD unknown2;
};

struct Value
{
private:
	DWORD dwReserved;
	WORD wType;	
	// Ignore this. It is version specific.  Use static variables as a hint.
	/*enum  {
			TYPE_UNAVAIL=-2,
			TYPE_UNSET=-1,
			TYPE_NULL=0,
			TYPE_INT=1,
			TYPE_BOOL=2,
			TYPE_ELEMENTREF=3,
			TYPE_ELEMENTLIST=4,
			TYPE_STRING=5,
			TYPE_POINT=6,
			TYPE_SIZE=7,
			TYPE_RECT=8,
			TYPE_COLOR=9,
			TYPE_LAYOUT=10,
			TYPE_GRAPHIC=11,
			TYPE_PROPERTYSHEET=12,
			TYPE_EXPRESSION=13,
			TYPE_ATOM=14,
			TYPE_ICON=15,
			TYPE_RLE=16,
			TYPE_UNKNOWN=17,
			TYPE_GUID=18,
			TYPE_VALUELIST=19,
			TYPE_COLORIZE=20,
			TYPE_DFC=21,
			TYPE_DTB=22,
			TYPE_ANIMATION=23
		};*/

	DWORD dwRefCount;
	union {
		int intVal;
		BOOL boolVal;
		void *pElementVal;
		DynamicArray *elementListVal;
		WCHAR *stringVal;
		POINT pointVal;
		SIZE sizeVal;
		RECT rectVal;
		struct colorVal {
			DWORD dwNotYetKnown1;
			COLORREF argb;
			DWORD dwNotYetKnown3;
			DWORD dwNotYetKnown4;
		}pColor;
		//Layout *layoutVal;
		struct Graphic {
			struct {
				HBITMAP hAlphaProcessedBitmap; // ProcessAlphaBitmapI
				DWORD dwsomethingelse;
				DWORD dwsomethingelse2;
				WORD wWidth;
				WORD wHeight;
				BYTE flag; // Really only a byte but should be DWORD sized due to 32 bit alignment
				DWORD Idunno; // So far always FFFFFFFF
				WORD stretchleft;
				WORD stretchtop;
				WORD stretchright;
				WORD stretchbottom;
				DWORD dwUnk1;
				DWORD dwUnk2;
				// There are other things that I've left out. (12 dwords)
			} *info;
			DWORD one;
			DWORD big;
			DWORD mystery;
		}pGraphic;
		//PropertySheetW *propertysheetVal;
		//Expression *expressionVal;
		ATOM atomVal;
		HICON iconVal;
		struct rleVal {
			void *pRLEData;
			void *pDualRLEData;
			BYTE bUnknown; //Set to 0xFE if created with data-pointer, or 1 if created with filename
		} rleVal;
		IUnknown *unknownVal;
		GUID guidVal;
		DynamicArray *valueListVal;
		DWORD colorizeVal;
		struct DFCVal {
			unsigned int arg1;
			unsigned int arg2;
		};
		struct DBTVal {
			unsigned int arg1;
			int arg2;
			int arg3;
			Value *arg4;
		};
		struct AnimationVal {
			BYTE arg4;
			BYTE arg1;
			DynamicArray* arg2;
			DynamicArray* arg3;
		};
	};
};

struct HwndHost
{
	void * pVtbl;
	void * pGunk;
	void * pHwndElement;
};

class CDUICompat
{
public:
	CDUICompat(HWND hWnd);
	~CDUICompat(void);

	void ReleaseValue(void * pValue)
	{
		if (m_pfnValueRelease)
		{
			CallFunction(m_pfnValueRelease,CT_THISCALL,1,pValue);
			return;
		}
		Value * pv = (Value*)pValue;
		DWORD dwRefCount = ((DWORD*)pValue)[m_nOffsetToValueRefCount/4];
		if (dwRefCount != -1)
		{
			dwRefCount--;
			((DWORD*)pValue)[m_nOffsetToValueRefCount/4] = dwRefCount;
		}
		if (dwRefCount == 0)
		{
			if (m_pfnValueZeroRelease)
				CallFunction(m_pfnValueZeroRelease,CT_THISCALL,1,pValue);
		}
	}

	Value * ElementGetValue(void * pElement, void * pPropertyInfo, int nType)
	{
		Value * pRet = 0;
		__try
		{
			if (m_pfnGetElementValue1)
			{
				pRet = (Value*)CallFunction(m_pfnGetElementValue1,CT_THISCALL,3,pElement,pPropertyInfo,nType);
				return pRet;
			}
			if (m_pfnGetElementValue2)
			{
				pRet = (Value*)CallFunction(m_pfnGetElementValue2,CT_THISCALL,4,pElement,pPropertyInfo,nType,0);
				return pRet;
			}
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
			pRet = 0;
		}
		return pRet;
	}
	
	void * GetIDProp()	{ return m_pIdProp; }
	void * GetChildrenProp() { return m_pChildrenProp; }
	void * GetClassProp() { return m_pClassProp; }
	void * GetContentProp() { return m_pContentProp; }

	void GetElementRect( void * pElement, LPRECT lpRect)
	{
		ZeroMemory(lpRect,sizeof(RECT));
		__try
		{
			HGADGET * pGadgets = (HGADGET*)pElement;
			if (m_pfnGetGadgetRect)
			{
				if (!m_pfnGetGadgetRect(pGadgets[1],lpRect,0xC)) // 0xC is hard coded because I don't know what it means :P
				{
					char szError[1024];
					sprintf_s(szError,1024,"GetGadgetRect failed - HGADGET: 0x%08x - Error:0x%08x\r\n",pGadgets[1],GetLastError());
					OutputDebugString(szError);
				}
			}
			else
				OutputDebugString("Failed to find GetGadgetRect\r\n");
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
			OutputDebugString("Caught Exception in GetElementRect\r\n");
		}
	}

	IUnknown * ElementGetDataContext( void * pElement, Value ** ppValue )
	{
		__try
		{
			if (m_pfnElementGetDataContext)
			{
				return (IUnknown*)CallFunction(m_pfnElementGetDataContext,CT_THISCALL,2,pElement, ppValue);
			}
			if (ppValue)
				*ppValue= 0;
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
		
		}
		return 0;
	}

	void * GetHwndElement(void * pHwndHost)
	{
		if (m_pfnGetHwndElement)
		{
			return CallFunction(m_pfnGetHwndElement,CT_THISCALL,1,pHwndHost);
		}
		return ((HwndHost*)pHwndHost)->pHwndElement;
	}

	LPWSTR ValueToString(void * pValue, LPWSTR lpwszStr, UINT nLen)
	{
		if (m_pfnValueToString)
		{
			//DebugBreak();
			return (LPWSTR)CallFunction(m_pfnValueToString,CT_THISCALL,3,pValue,lpwszStr,nLen);
		}
		return 0;
	}

	IClassInfo * ElementGetClassInfo(void * pElement)
	{
		__try
		{
			int n = 0;
			if (m_hUxCore)
			{
				void * pGetClassInfo = (void*)GetProcAddress(m_hUxCore,"?GetClassInfoW@WLEditT@@UAEPAUIClassInfo@DirectUI@@XZ");
				void * pCreateTextHost = (void*)GetProcAddress(m_hUxCore,"?CreateTextHost@WLEditT@@MAEJXZ");
				BYTE * pWLEditTByte = (BYTE*)GetProcAddress(m_hUxCore,"??0WLEditT@@QAE@XZ");
				if (pGetClassInfo && pWLEditTByte)
				{
					int i =0;
					BOOL bFound = FALSE;
					for (;;i++)
					{
						if (pWLEditTByte[i]==0xC7 && pWLEditTByte[i+1]==0x06) // MOV DWORD PTR DS:[ESI],
						{
							if (!bFound)
								bFound = TRUE; // First one is DirectUI::Element::~vtbl
							else
							{
								DWORD * pWLEditTVtbl = *(DWORD**)(pWLEditTByte+i+2);
								for (int nIndex =0; nIndex<50; nIndex++)
								{
									if (pWLEditTVtbl[nIndex]==(DWORD)pGetClassInfo)
									{
										n = nIndex;
										break;
									}
									if (pWLEditTVtbl[nIndex]==(DWORD)pCreateTextHost)
									{
										break; // Couldn't find GetClassInfoW
									}
								}
							}
						}
						if (n!=0)
							break; // function found.
						if (pWLEditTByte[i]==0xCC || pWLEditTByte[i]==0xC3) // int 3 || retn
							break;
					}
				}
			}
			
			if (n==0)
			{
				if (m_hMsnCore || m_hUxCore)
					n =28;
				else
					n = 22;
			}

			DWORD * pVtbl = *((DWORD **)pElement);
			if (pVtbl[n])
				return (IClassInfo*)CallFunction((void*)pVtbl[n],CT_THISCALL,1,pElement);
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
		
		}
		return 0;
	}

	long InitThread()
	{
		__try
		{
			if (m_pfnInitThread1)
			{
				if (m_hMsnCore)
				{
					FARPROC fp = GetProcAddress(m_hMsnCore,"MsnCoreInitThread");
					if (fp)
						return fp();
					else
						return m_pfnInitThread1(2);
				}
				else if (m_hUxCore)
				{
					FARPROC fp = GetProcAddress(m_hUxCore,"UXCoreInitThread");
					if (fp)
						return fp();
					else
						return m_pfnInitThread1(2);
				}
				else
					return m_pfnInitThread1(2);
			}
			if (m_pfnInitThread2)
			{
				m_pfnInitThread2();
				HMODULE hDuser = GetModuleHandle("duser.dll");
				if (hDuser)
				{
					INITGADGETCOMPONENT InitGadgetComponent = (INITGADGETCOMPONENT)GetProcAddress(hDuser,"InitGadgetComponent");
					if (InitGadgetComponent)
					{
						InitGadgetComponent(3);
						InitGadgetComponent(2);
					}
				}
				return 0;
			}
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
		
		}
		return 0;
	}

	WORD GetValueType(Value* pValue)
	{
		DWORD * pdwValues = (DWORD*)pValue;
		return m_bOffsetHiWord?HIWORD(pdwValues[m_nOffsetToValueType/4]):LOWORD(pdwValues[m_nOffsetToValueType/4]);
	}

	void * GetValueValue(Value * pValue)
	{
		DWORD * pdwValues = (DWORD*)pValue;
		return (void*)pdwValues[m_nOffsetToValueValue/4];
	}

	int TYPE_ELEMENTLIST() {return m_nElementListType; }
	int TYPE_ELEMENT() { return m_nElementType; }
	int TYPE_ATOM() { return m_nAtomType; }
	int TYPE_BOOL() { return m_nBoolType; }
	int TYPE_INT() { return m_nIntType; }
	int TYPE_STRING() { return m_nStringType; }
private:
/*
?_ZeroRelease@Value@DirectUI@@AAEXXZ
?ChildrenProp@Element@DirectUI@@2PAUPropertyInfo@2@A
?ClassProp@Element@DirectUI@@2PAUPropertyInfo@2@A
?ContentProp@Element@DirectUI@@2PAUPropertyInfo@2@A

/*************Iffy***************
?GetDataContext@Element@DirectUI@@QAEPAUIUnknown@@PAPAVValue@2@@Z
?GetElement@NativeHWNDHost@DirectUI@@QAEPAVElement@2@XZ
/*************Iffy***************

?GetValue@Element@DirectUI@@QBEPAVValue@2@PBUPropertyInfo@2@H@Z // MSNCore
?GetValue@Element@DirectUI@@QAEPAVValue@2@PAUPropertyInfo@2@HPAUUpdateCache@2@@Z // AppWiz.cpl


?Release@Value@DirectUI@@QAEXXZ

?InitThread@DirectUI@@YGJXZ
?InitThread@DirectUI@@YGJI@Z

?IDProp@Element@DirectUI@@2PAUPropertyInfo@2@A
?ToString@Value@DirectUI@@QAEPA_WPA_WI@Z // MSNCore wchar_t*
?ToString@Value@DirectUI@@QAEPAGPAGI@Z  // AppWiz.cpl  unsigned short*


22
26

MsnCoreInitProcess
MsnCoreInitThread
MsnCoreUnInitProcess
MsnCoreUnInitThread
*/

	void * m_pIdProp;
	void * m_pChildrenProp;
	void * m_pClassProp;
	void * m_pContentProp;

	INITTHREAD1 m_pfnInitThread1;
	INITTHREAD2 m_pfnInitThread2;
	ELEMENTGETDATACONTEXT m_pfnElementGetDataContext;
	VALUEZERORELEASE m_pfnValueZeroRelease;
	VALUERELEASE	m_pfnValueRelease;
	GETELEMENTVALUE1 m_pfnGetElementValue1;
	GETELEMENTVALUE2 m_pfnGetElementValue2;
	GETHWNDELEMENT m_pfnGetHwndElement;
	VALUETOSTRING m_pfnValueToString;
	GETGADGETRECT m_pfnGetGadgetRect;

	BOOL m_bSymLoaded;
	HMODULE m_hMsnCore;
	HMODULE m_hUxCore;

	int m_nElementListType;
	int m_nElementType;
	int m_nAtomType;
	int m_nBoolType;
	int m_nIntType;
	int m_nStringType;

	int m_nOffsetToValueType;
	int m_nOffsetToValueRefCount;
	int m_nOffsetToValueValue;
	BOOL m_bOffsetHiWord;
};
